﻿' Avian Play Playlist Entry
Imports System.Drawing
Imports AvianWaves.AvianPlay.Language

Public Class AVPLPlaylistEntry
  Implements MobilePrettyGui.iPrettyListItem
  Implements ICloneable
  Implements IDisposable
  Implements IComparable(Of AVPLPlaylistEntry)


#Region " Shared Properties "
  Private Shared _SearchTerm As String = "" ' This property is used to render search highlights
  Public Shared Property SearchTerm() As String
    Get
      Return _SearchTerm
    End Get
    Set(ByVal value As String)
      _SearchTerm = value
    End Set
  End Property

  ' Current playing item - shared so it can be accessed by the AvianPlayMain and this class; used here to render the correct "now playing" colors and style.  Used in AvianPlayMain for numerous things!
  Private Shared _currentPlayingItem As AVPLPlaylistEntry = Nothing
  Public Shared Property CurrentPlayingItem() As AVPLPlaylistEntry
    Get
      Return _currentPlayingItem
    End Get
    Set(ByVal value As AVPLPlaylistEntry)
      _currentPlayingItem = value
    End Set
  End Property

  Public Enum AlbumArtListDisplayModeEnum
    NoAlbumArt = 0
    AutomaticIndent = 1
    AlwaysIndent = 2
  End Enum
  Private Shared _AlbumArtListDisplayMode As AlbumArtListDisplayModeEnum
  Public Shared Property AlbumArtListDisplayMode() As AlbumArtListDisplayModeEnum
    Get
      Return _AlbumArtListDisplayMode
    End Get
    Set(ByVal value As AlbumArtListDisplayModeEnum)
      _AlbumArtListDisplayMode = value
    End Set
  End Property
#End Region

  ' General
  Private _Type As EntryType                    ' Is this entry a file, stream, or meta type?
  Private _isSelected As Boolean                ' Is this entry selected?
  Private _hashCode As Integer                  ' Used to uniquely identify this instance

  ' File data
  Private _Title As String = ""                 ' File specifiTitle of track
  Private _Artist As String = ""                ' File specific Artist entry
  Private _FilePath As String = ""              ' File location of the entry

  ' Placeholders for file data that is retrieved from the database
  Private _AlbumArtist As String = ""           ' DB's entry for album artist
  Private _Album As String = ""                 ' DB's entry for album
  Private _Genre As String = ""                 ' DB's entry for genre
  Private _hasThumbnail As Boolean = True       ' DB's entry for whether or not a thumbnail exists, independent of if this entry has an image embedded in it; always set to TRUE if we don't know (just in case)
  Private _thumbnail As Bitmap = Nothing        ' Thumbnail image that represents this entry

  ' Stream data
  Private _StreamTitle As String = ""           ' Actual title reported by the stream (_Title may be a user defined value)
  Private _StreamSubtitle As String = ""        ' Some stream systems (like shoutcast) have additional information that can be stored here
  Private _StreamPlaylistUrl As Uri = Nothing   ' URL used to retrieve the PLS/M3U/etc file that points to the available streams
  Private _StreamUsePlaylistUrlOnce As Boolean = False ' If this is true, then the StreamPlaylistUrl can only be used once; This is used for situations like ShoutCast, where the tune-in URL can (and does) change frequently.  This ensures that subsequent refreshes are not able to replace the stream URL list with URLs that don't work.
  Private _StreamURLs As New List(Of Uri)       ' Complete list of URLs that can be used for the stream
  Private _LastStreamRefresh As Date = Date.MinValue

  ' Meta data
  Private _MetaQuery As String = ""             ' The meta query used for meta entries from database (example: GENRE=PODCAST)
  Private _MetaResults As New List(Of AVPLPlaylistEntry)  ' The result set from the meta search (children cannot be of type META)
  Private _LastMetaRefresh As Date = Date.MinValue

  ' Sorting data
  Private _sortMode As SortMode = SortMode.Text

  ' UI: Private
  Private _invalidateHeight As Boolean = True
  Private _height As Integer = 0
  Private _clickRectangle As Drawing.Rectangle
  Private _ignoreLoadedItem As Boolean = False

  ' UI: Shared amongst all same entities
  Private Shared slc As MobilePrettyGui.PrettyList.ListColors
  Private Shared sbrBg As Brush = Nothing  ' Background normal
  Private Shared sbrBgA As Brush = Nothing ' Background alternate
  Private Shared sbrBgS As Brush = Nothing ' Background selected
  Private Shared sbrFg As Brush = Nothing  ' Foreground normal
  Private Shared sbrFg2 As Brush = Nothing ' Foreground line 2
  Private Shared sbrFgP As Brush = Nothing ' Foreground "is playing"
  Private Shared sbrHL As Brush = Nothing  ' Foreground match highlight
  Private Shared sbrFgS As Brush = Nothing ' Foreground selected
  Private Shared sfR As New Drawing.Font("Tahoma", 9, Drawing.FontStyle.Regular)
  Private Shared sfB As New Drawing.Font("Tahoma", 10, Drawing.FontStyle.Bold)

  ' Hash code incrementer used to uniquely identify each instance; shared
  Private Shared _hashIndex As Integer = Integer.MinValue


  Public Enum EntryType
    File = 0
    Stream = 1
    Meta = 2
  End Enum

  Friend Enum SortMode
    Text = 0
    Subtext = 1
    [Type] = 2
  End Enum

  Public Sub New()
    Me._hashCode = _hashIndex

    If _hashIndex < Integer.MaxValue Then
      _hashIndex += 1
    Else
      _hashIndex = Integer.MinValue
    End If
  End Sub

  Public Sub New(ByRef avple As AVPLPlaylistEntry, Optional ByVal copyThumbnail As Boolean = False)
    Me.New()
    Me.CopyInto(avple, copyThumbnail)
  End Sub

  Public Sub CopyInto(ByRef avple As AVPLPlaylistEntry, Optional ByVal copyThumbnail As Boolean = False)
    Me._Title = avple._Title
    Me._Type = avple._Type
    Me._Artist = avple._Artist
    Me._FilePath = avple._FilePath

    Me._AlbumArtist = avple._AlbumArtist
    Me._Album = avple._Album
    Me._Genre = avple._Genre
    Me._hasThumbnail = avple._hasThumbnail

    Me._StreamTitle = avple._StreamTitle
    Me._StreamSubtitle = avple._StreamSubtitle

    If avple._StreamPlaylistUrl IsNot Nothing Then
      Me._StreamPlaylistUrl = New Uri(avple._StreamPlaylistUrl, "")
    End If

    Me._StreamURLs = New List(Of Uri)(avple._StreamURLs)
    Me._StreamUsePlaylistUrlOnce = avple._StreamUsePlaylistUrlOnce
    Me._LastStreamRefresh = avple._LastStreamRefresh
    Me._MetaQuery = avple.MetaQuery
    Me._MetaResults = New List(Of AVPLPlaylistEntry)(avple._MetaResults)
    Me._LastMetaRefresh = avple._LastMetaRefresh

    If copyThumbnail AndAlso avple._thumbnail IsNot Nothing Then
      Me._thumbnail = New Bitmap(avple._thumbnail)
    Else
      Me._thumbnail = Nothing
    End If
  End Sub

  Public Shared Sub SetColors(ByVal lc As MobilePrettyGui.PrettyList.ListColors)
    If sbrBg Is Nothing OrElse slc.BackgroundColor <> lc.BackgroundColor Then
      sbrBg = New SolidBrush(lc.BackgroundColor)
    End If
    If sbrBgA Is Nothing OrElse slc.BackgroundColorAlternate <> lc.BackgroundColorAlternate Then
      sbrBgA = New SolidBrush(lc.BackgroundColorAlternate)
    End If
    If sbrBgS Is Nothing OrElse slc.BackgroundColorSelected <> lc.BackgroundColorSelected Then
      sbrBgS = New SolidBrush(lc.BackgroundColorSelected)
    End If
    If sbrBgS Is Nothing OrElse slc.BackgroundColorSelected <> lc.BackgroundColorSelected Then
      sbrBgS = New SolidBrush(lc.BackgroundColorSelected)
    End If
    If sbrFg Is Nothing OrElse slc.TextColor1 <> lc.TextColor1 Then
      sbrFg = New SolidBrush(lc.TextColor1)
    End If
    If sbrFg2 Is Nothing OrElse slc.TextColor2 <> lc.TextColor2 Then
      sbrFg2 = New SolidBrush(lc.TextColor2)
    End If
    If sbrHL Is Nothing OrElse slc.TextColor3 <> lc.TextColor3 Then
      sbrHL = New SolidBrush(lc.TextColor3)
    End If
    If sbrFgP Is Nothing OrElse slc.TextColor4 <> lc.TextColor4 Then
      sbrFgP = New SolidBrush(lc.TextColor4)
    End If
    If sbrFgS Is Nothing OrElse slc.TextcolorSelected <> lc.TextcolorSelected Then
      sbrFgS = New SolidBrush(lc.TextcolorSelected)
    End If

    slc = lc
  End Sub

  <Xml.Serialization.XmlIgnore()> _
  Public ReadOnly Property Text() As String
    Get
      Select Case Me._Type
        Case EntryType.File
          Return Me._Title
        Case EntryType.Stream
          Return CStr(IIf(Me._Title.Trim.Length > 0, Me._Title, Me._StreamTitle))
        Case EntryType.Meta
          Return "META"
      End Select
      Return ""
    End Get
  End Property

  <Xml.Serialization.XmlIgnore()> _
  Public ReadOnly Property Subtext() As String
    Get
      Select Case Me._Type
        Case EntryType.File
          If Me._Album.Length > 0 Then
            If Me._Artist.Length > 0 AndAlso Me._AlbumArtist.Length > 0 AndAlso Me._Artist <> Me._AlbumArtist Then
              Return Me._Artist & " [" & Me._AlbumArtist & " - " & Me._Album & "]"
            ElseIf Me._Artist.Length > 0 Then
              Return Me._Artist & " - " & Me._Album
            ElseIf Me._AlbumArtist.Length > 0 Then
              Return Me._AlbumArtist & " - " & Me._Album
            Else
              Return Me._Album
            End If
          ElseIf Me._Artist.Length > 0 Then
            Return Me._Artist
          Else
            Return Me._AlbumArtist
          End If
        Case EntryType.Stream
          Return Me._StreamSubtitle
        Case EntryType.Meta
          Return Me._MetaQuery
      End Select
      Return ""
    End Get
  End Property


  Public Property Title() As String
    Get
      Return Me._Title
    End Get
    Set(ByVal value As String)
      Me._Title = value
      Me._invalidateHeight = True
    End Set
  End Property

  Public Property Type() As EntryType
    Get
      Return Me._Type
    End Get
    Set(ByVal value As EntryType)
      Me._Type = value
      Me._invalidateHeight = True
    End Set
  End Property

  <Xml.Serialization.XmlIgnore()> _
  Public Property HasDBThumbnail() As Boolean
    Get
      Return Me._hasThumbnail
    End Get
    Set(ByVal value As Boolean)
      Me._hasThumbnail = value
    End Set
  End Property

  <Xml.Serialization.XmlIgnore()> _
  Public Property Thumbnail() As Bitmap
    Get
      Return Me._thumbnail
    End Get
    Set(ByVal value As Bitmap)
      If Me._thumbnail IsNot Nothing Then
        Me._thumbnail.Dispose()
        Me._thumbnail = Nothing
      End If
      Me._thumbnail = New Bitmap(value)
    End Set
  End Property

  <Xml.Serialization.XmlIgnore()> _
  Public ReadOnly Property IsStream() As Boolean
    Get
      Return (Me._Type = EntryType.Stream)
    End Get
  End Property

  <Xml.Serialization.XmlIgnore()> _
  Public ReadOnly Property IsFile() As Boolean
    Get
      Return (Me._Type = EntryType.File)
    End Get
  End Property

  <Xml.Serialization.XmlIgnore()> _
  Public ReadOnly Property IsMeta() As Boolean
    Get
      Return (Me._Type = EntryType.Meta)
    End Get
  End Property

  Public Property Artist() As String
    Get
      Return Me._Artist
    End Get
    Set(ByVal value As String)
      Me._Artist = value
      Me._invalidateHeight = True
    End Set
  End Property

  Public Property FilePath() As String
    Get
      Return Me._FilePath
    End Get
    Set(ByVal value As String)
      Me._FilePath = value
      Me._invalidateHeight = True
    End Set
  End Property

  <Xml.Serialization.XmlIgnore()> _
  Public Property AlbumArtist() As String
    Get
      Return Me._AlbumArtist
    End Get
    Set(ByVal value As String)
      Me._AlbumArtist = value
    End Set
  End Property

  <Xml.Serialization.XmlIgnore()> _
  Public Property Album() As String
    Get
      Return Me._Album
    End Get
    Set(ByVal value As String)
      Me._Album = value
    End Set
  End Property

  <Xml.Serialization.XmlIgnore()> _
  Public Property Genre() As String
    Get
      Return Me._Genre
    End Get
    Set(ByVal value As String)
      Me._Genre = value
    End Set
  End Property

  Public Property StreamTitle() As String
    Get
      Return Me._StreamTitle
    End Get
    Set(ByVal value As String)
      Me._StreamTitle = value
      Me._invalidateHeight = True
    End Set
  End Property

  Public Property StreamSubtitle() As String
    Get
      Return Me._StreamSubtitle
    End Get
    Set(ByVal value As String)
      Me._StreamSubtitle = value
      Me._invalidateHeight = True
    End Set
  End Property

  Public Property StreamPlaylistUrl() As Uri
    Get
      Return Me._StreamPlaylistUrl
    End Get
    Set(ByVal value As Uri)
      If value IsNot Nothing Then
        Me._StreamPlaylistUrl = New Uri(value, "") ' This constructs a new URI instead of setting a reference which might get updated elsewhere
      Else
        Me._StreamPlaylistUrl = Nothing
      End If
    End Set
  End Property

  <Xml.Serialization.XmlIgnore()> _
  Public Property StreamPlaylistUrlString() As String
    Get
      If Me._StreamPlaylistUrl IsNot Nothing Then
        Return Me._StreamPlaylistUrl.ToString
      Else
        Return ""
      End If
    End Get
    Set(ByVal value As String)
      If value <> String.Empty Then
        Me._StreamPlaylistUrl = New Uri(value)
      Else
        Me._StreamPlaylistUrl = Nothing
      End If
    End Set
  End Property

  Public Property StreamUsePlaylistUrlOnce() As Boolean
    Get
      Return Me._StreamUsePlaylistUrlOnce
    End Get
    Set(ByVal value As Boolean)
      Me._StreamUsePlaylistUrlOnce = value
    End Set
  End Property

  Public Property StreamURLs() As List(Of Uri)
    Get
      Return Me._StreamURLs
    End Get
    Set(ByVal value As List(Of Uri))
      Me._StreamURLs = value
    End Set
  End Property

  Public Sub SetFirstStreamURL(ByVal streamURL As Uri)
    If streamURL IsNot Nothing Then
      SyncLock Me._StreamURLs ' Lock in case async operation tries to touch this variable while we are reording it
        If Me._StreamURLs.Count > 0 AndAlso Uri.Compare(Me._StreamURLs(0), streamURL, UriComponents.AbsoluteUri, UriFormat.Unescaped, StringComparison.Ordinal) <> 0 Then
          Dim skipStreamURLs As New List(Of Uri)
          Dim newStreamURLs As New List(Of Uri)

          For Each u As Uri In Me._StreamURLs
            If newStreamURLs.Count > 0 OrElse Uri.Compare(u, streamURL, UriComponents.AbsoluteUri, UriFormat.Unescaped, StringComparison.Ordinal) = 0 Then
              newStreamURLs.Add(u)
            Else
              skipStreamURLs.Add(u)
            End If
          Next

          Me._StreamURLs.Clear()

          For Each u As Uri In newStreamURLs
            Me._StreamURLs.Add(u)
          Next

          For Each u As Uri In skipStreamURLs
            Me._StreamURLs.Add(u)
          Next
        End If
      End SyncLock
    End If
  End Sub

  Public Property LastStreamRefresh() As Date
    Get
      Return Me._LastStreamRefresh
    End Get
    Set(ByVal value As Date)
      Me._LastStreamRefresh = value
    End Set
  End Property

  Public Sub RefreshStreamURLsFromPlaylistURL()
    If Me._StreamPlaylistUrl IsNot Nothing AndAlso Me._StreamPlaylistUrl.IsWellFormedOriginalString Then
      Dim oldUrls As List(Of Uri) = Me._StreamURLs
      Try
        Dim req As System.Net.HttpWebRequest = System.Net.HttpWebRequest.Create(Me._StreamPlaylistUrl)
        Dim resp As System.Net.HttpWebResponse = req.GetResponse
        Dim pl As StandardPlaylist = PlaylistReader.ParsePLSFile(resp.GetResponseStream())
        resp.Close()

        If pl.Entries.Count > 0 AndAlso Not pl.ErrorsOnLoad Then
          Me._StreamURLs = New List(Of Uri)
          For Each ple As StandardPlaylistEntry In pl.Entries
            Me._StreamURLs.Add(New Uri(ple.Location))
          Next
        End If

        If Me._StreamURLs.Count <= 0 Then
          ' If we didn't get any new URLs, reassign the old ones
          Me._StreamURLs = oldUrls
        End If

        ' If this is a 'use once' url, clear out the url now that we are done; this happens even if no results are found from the playlist that was returned
        If Me._StreamUsePlaylistUrlOnce Then
          Me._StreamPlaylistUrl = Nothing
          Me._StreamUsePlaylistUrlOnce = False
        End If
      Catch ex As Threading.ThreadAbortException
        Me._StreamURLs = oldUrls
      Catch ex As Exception
        ' An error occurred, so reassign the old URLs
        Me._StreamURLs = oldUrls
        ErrorLog.WriteToErrorLog("RefreshStreamURLsFromPlaylistURL() :: " & ex.ToString)
        Throw
      Finally
        ' Set the refresh time, no matter what happens in here, because we don't want to constantly refresh due to invalid URL or something
        Me._LastStreamRefresh = Date.Now
      End Try
    End If
  End Sub

  Public Property MetaQuery() As String
    Get
      Return Me._MetaQuery
    End Get
    Set(ByVal value As String)
      Me._MetaQuery = value
    End Set
  End Property

  Public Property MetaResults() As List(Of AVPLPlaylistEntry)
    Get
      Return Me._MetaResults
    End Get
    Set(ByVal value As List(Of AVPLPlaylistEntry))
      Me._MetaResults = value
    End Set
  End Property

  Public Property LastMetaRefresh() As Date
    Get
      Return Me._LastMetaRefresh
    End Get
    Set(ByVal value As Date)
      Me._LastMetaRefresh = value
    End Set
  End Property

  Public Sub RefreshMetaSearch()
    ' TODO: Meta refresh search Stuff....
    Me._LastMetaRefresh = Date.Now
  End Sub

  Friend Property SortBy() As SortMode
    Get
      Return Me._sortMode
    End Get
    Set(ByVal value As SortMode)
      Me._sortMode = value
    End Set
  End Property

  Private ReadOnly Property SortString() As String
    Get
      Select Case Me._sortMode
        Case SortMode.Type
          If Me._Type = EntryType.File Then
            Return "A"
          ElseIf Me._Type = EntryType.Stream Then
            Return "B"
          Else
            Return "C"
          End If
        Case SortMode.Subtext
          Return Me.Subtext
        Case Else
          Return Me.Text
      End Select
    End Get
  End Property

  Private ReadOnly Property SortString2() As String
    Get
      Select Case Me._sortMode
        Case SortMode.Type, SortMode.Subtext
          Return Me.Text
        Case Else
          Return Me.Subtext
      End Select
    End Get
  End Property

  Private ReadOnly Property SortString3() As String
    Get
      Select Case Me._sortMode
        Case SortMode.Text, SortMode.Subtext
          If Me._Type = EntryType.File Then
            Return "A"
          ElseIf Me._Type = EntryType.Stream Then
            Return "B"
          Else
            Return "C"
          End If
        Case Else
          Return Me.Subtext
      End Select
    End Get
  End Property

  Public Shared Sub DrawHighlight(ByVal str As String, ByVal f As Drawing.Font, ByVal brFg As Drawing.Brush, ByVal brHL As Drawing.Brush, ByVal g As Drawing.Graphics, ByVal x As Integer, ByVal y As Integer)
    Dim lastIndex As Integer = str.IndexOf(_SearchTerm, StringComparison.CurrentCultureIgnoreCase)
    Dim remainder As Integer = 0

    ' Draw the Title
    While lastIndex >= 0
      ' Fetch the segment until the next highlight
      Dim strseg As String = str.Substring(0, lastIndex)

      ' Measure the segment until the highlight
      With g.MeasureString(strseg, f)
        ' Draw the segment
        g.DrawString(strseg, f, brFg, x, y)

        ' Increment X
        x += .Width
      End With

      ' Fetch the highlighted part
      Dim strseghl As String = str.Substring(lastIndex, _SearchTerm.Length)

      ' Measure highlight
      With g.MeasureString(strseghl, f)
        ' Draw the segment
        g.DrawString(strseghl, f, brHL, x, y)

        ' Increment X
        x += .Width
      End With

      ' Crop the string forward
      str = str.Substring(lastIndex + _SearchTerm.Length)

      ' Fetch next idnex
      lastIndex = str.IndexOf(_SearchTerm, StringComparison.CurrentCultureIgnoreCase)
    End While

    ' Draw the rest!
    g.DrawString(str, f, brFg, x, y)
  End Sub

  Public Sub DrawPrettyListItem(ByVal g As System.Drawing.Graphics, ByVal dpiFactor As Single, ByVal colors As MobilePrettyGui.PrettyList.ListColors, ByVal rect As System.Drawing.Rectangle, ByVal isAlternate As Boolean) Implements MobilePrettyGui.iPrettyListItem.DrawPrettyListItem
    ' Check shared colors
    SetColors(colors)

    Dim sfTitle As Drawing.SizeF
    Dim sfSubTitle As Drawing.SizeF
    Dim sfType As Drawing.SizeF

    Dim strTitle As String = Me.Text
    Dim strSubtitle As String = Me.Subtext
    Dim strType As String

    Select Case Type
      Case EntryType.Stream
        strType = GetLanguageString("Stream")
      Case Else
        strType = ""
    End Select

    Dim brBg As Brush
    Dim brBgA As Brush
    Dim brFg As Brush
    Dim brFg2 As Brush
    Dim brFgT As Brush
    Dim brHL As Brush

    If Me._isSelected Then
      brBg = sbrBgS
      brBgA = sbrBgS
      brHL = sbrFgS
      brFg = sbrFgS
      brFg2 = sbrFgS
      brFgT = sbrFgS
    Else
      brBg = sbrBg
      brBgA = sbrBgA
      brHL = sbrHL
      brFg = sbrFg
      brFg2 = sbrFg2
      brFgT = sbrFg2
    End If

    If Not Me._ignoreLoadedItem AndAlso Me.Equals(_currentPlayingItem) Then
      strType = GetLanguageString("Loaded")
      If Not Me._isSelected Then
        brFgT = sbrFgP
      End If
    End If

    sfTitle = g.MeasureString(strTitle, sfB)
    sfSubTitle = g.MeasureString(strSubtitle, sfR)
    sfType = g.MeasureString(strType, sfR)
    sfType.Width += (dpiFactor * 8) ' Add padding in case of scroll bar line

    g.FillRectangle(IIf(isAlternate, brBgA, brBg), rect)

    ' Draw the thumbnail
    Dim txtPadWidth As Integer = 0

    If _AlbumArtListDisplayMode <> AlbumArtListDisplayModeEnum.NoAlbumArt Then
      Dim imgWidth As Integer = rect.Height - (4 * dpiFactor)
      Dim imgHeight As Integer
      Dim imgY As Integer = 0

      If Me._thumbnail IsNot Nothing Then
        imgHeight = (imgWidth / Me._thumbnail.Width) * Me._thumbnail.Height
        If imgHeight < Me._height Then imgY = (Me._height - imgHeight) >> 1 ' Vertically center the album art if it's 'wide'
        g.DrawImage(Me._thumbnail, New Rectangle(rect.X + (2 * dpiFactor), rect.Y + imgY, imgWidth, imgHeight), New Rectangle(0, 0, Me._thumbnail.Width, Me._thumbnail.Height), GraphicsUnit.Pixel)
        txtPadWidth = rect.X + rect.Height + (2 * dpiFactor)
      ElseIf _AlbumArtListDisplayMode = AlbumArtListDisplayModeEnum.AlwaysIndent Then
        ' TODO: Draw "no thumbnail" thumbnail
        txtPadWidth = rect.X + rect.Height + (2 * dpiFactor)
      End If
    End If

    If _SearchTerm.Length > 0 Then
      DrawHighlight(strTitle, sfB, brFg, brHL, g, rect.X + txtPadWidth, rect.Y)
      DrawHighlight(strSubtitle, sfR, brFg2, brHL, g, rect.X + txtPadWidth, rect.Y + sfTitle.Height + (dpiFactor * 2))
    Else
      g.DrawString(strTitle, sfB, brFg, rect.X + txtPadWidth, rect.Y)
      g.DrawString(strSubtitle, sfR, brFg2, rect.X + txtPadWidth, rect.Y + sfTitle.Height + (dpiFactor * 2))
    End If

    If strType <> String.Empty Then
      g.FillRectangle(IIf(isAlternate, brBgA, brBg), (rect.X + rect.Width) - (sfType.Width + 2), rect.Y + sfTitle.Height + (dpiFactor * 2), sfType.Width + 2, sfType.Height)
      g.DrawString(strType, sfR, brFgT, (rect.X + rect.Width) - sfType.Width, rect.Y + sfTitle.Height + (dpiFactor * 2))
    End If
  End Sub

  Public Function DrawPrettyListItemHeight(ByVal g As System.Drawing.Graphics, ByVal dpifactor As Single) As Integer Implements MobilePrettyGui.iPrettyListItem.DrawPrettyListItemHeight
    Static sfTitle As Drawing.SizeF = Drawing.SizeF.Empty
    Static sfSubtitle As Drawing.SizeF = Drawing.SizeF.Empty

    If Me._invalidateHeight OrElse sfTitle = Drawing.SizeF.Empty OrElse sfSubtitle = Drawing.SizeF.Empty OrElse Me._height <= 0 Then
      sfTitle = g.MeasureString("Pp", sfB)
      sfSubtitle = g.MeasureString("Pp", sfR)

      Me._height = sfTitle.Height + sfSubtitle.Height + (dpifactor * 6)

      Me._invalidateHeight = False
    End If

    Return Me._height
  End Function

  Public Property IsSelected() As Boolean Implements MobilePrettyGui.iPrettyListItem.IsSelected
    Get
      Return Me._isSelected
    End Get
    Set(ByVal value As Boolean)
      Me._isSelected = value
    End Set
  End Property

  <Xml.Serialization.XmlIgnore()> _
  Public Property ClickRectangle() As System.Drawing.Rectangle Implements MobilePrettyGui.iPrettyListItem.ClickRectangle
    Get
      Return Me._clickRectangle
    End Get
    Set(ByVal value As System.Drawing.Rectangle)
      Me._clickRectangle = value
    End Set
  End Property

  <Xml.Serialization.XmlIgnore()> _
  Public Property IgnoreLoadedItem() As Boolean
    Get
      Return Me._ignoreLoadedItem
    End Get
    Set(ByVal value As Boolean)
      Me._ignoreLoadedItem = value
    End Set
  End Property

  Public Sub OnMouseClick() Implements MobilePrettyGui.iPrettyListItem.OnMouseClick
    Me._isSelected = False
  End Sub

  Public Sub OnMouseLongClick() Implements MobilePrettyGui.iPrettyListItem.OnMouseLongClick
    Me._isSelected = False
  End Sub

  Public Function Clone() As Object Implements System.ICloneable.Clone
    Return New AVPLPlaylistEntry(Me)
  End Function

  Public Sub OnMouseDown() Implements MobilePrettyGui.iPrettyListItem.OnMouseDown
    Me._isSelected = True
  End Sub

  Public Sub OnMouseUp() Implements MobilePrettyGui.iPrettyListItem.OnMouseUp
    Me._isSelected = False
  End Sub

  Public Sub OnMouseScroll() Implements MobilePrettyGui.iPrettyListItem.OnMouseScroll
    Me._isSelected = False
  End Sub

  Public Overrides Function Equals(ByVal obj As Object) As Boolean
    If obj IsNot Nothing AndAlso obj.GetType Is GetType(AVPLPlaylistEntry) Then
      Dim ae As AVPLPlaylistEntry = CType(obj, AVPLPlaylistEntry)
      If Me.Type = ae.Type Then
        Select Case Me.Type
          Case EntryType.File
            Return String.Equals(Me.FilePath.Trim, ae.FilePath.Trim, StringComparison.CurrentCultureIgnoreCase)
          Case EntryType.Stream
            If Me.StreamPlaylistUrlString.Length > 0 AndAlso ae.StreamPlaylistUrlString.Length > 0 Then
              Return String.Equals(Me.StreamPlaylistUrlString, ae.StreamPlaylistUrlString, StringComparison.CurrentCultureIgnoreCase)
            ElseIf Me.StreamURLs.Count = ae.StreamURLs.Count Then
              For i As Integer = 0 To Me.StreamURLs.Count - 1
                If Not String.Equals(Me.StreamURLs(i).ToString, ae.StreamURLs(i).ToString, StringComparison.CurrentCultureIgnoreCase) Then
                  Return False
                End If
              Next
              Return True
            End If
          Case EntryType.Meta
            Return String.Equals(Me.MetaQuery.Trim, ae.MetaQuery.Trim, StringComparison.CurrentCultureIgnoreCase)
        End Select
      End If
    End If

    Return False
  End Function

  Public ReadOnly Property FilterIsIncluded(ByVal filter As String) As Boolean Implements MobilePrettyGui.iPrettyListItem.FilterIsIncluded
    Get
      Select Case Me._Type
        Case EntryType.File, EntryType.Stream
          If Me.Text.ToUpper.Contains(filter) Then Return True
          If Me.Subtext.ToUpper.Contains(filter) Then Return True
      End Select

      Return False
    End Get
  End Property

  Public Function CompareTo(ByVal other As AVPLPlaylistEntry) As Integer Implements System.IComparable(Of AVPLPlaylistEntry).CompareTo
    Dim comp As Integer = String.Compare(Me.SortString, other.SortString, True)

    If comp = 0 Then
      ' First tie breaker
      comp = String.Compare(Me.SortString2, other.SortString2, True)
    End If

    If comp = 0 Then
      ' Second tie breaker
      comp = String.Compare(Me.SortString3, other.SortString3, True)
    End If

    If comp = 0 Then
      ' Final tie breaker
      If Me.GetHashCode > other.GetHashCode Then
        comp = 1
      ElseIf Me.GetHashCode < other.GetHashCode Then
        comp = -1
      End If
    End If

    Return comp
  End Function

  Public Overrides Function GetHashCode() As Integer
    Return _hashCode
  End Function

#Region " IDisposable Support "
  Private disposedValue As Boolean = False    ' To detect redundant calls
  ' IDisposable
  Protected Overridable Sub Dispose(ByVal disposing As Boolean)
    If Not Me.disposedValue Then
      If disposing Then
        If Me._thumbnail IsNot Nothing Then
          Me._thumbnail.Dispose()
          Me._thumbnail = Nothing
        End If
      End If
    End If
    Me.disposedValue = True
  End Sub

  ' This code added by Visual Basic to correctly implement the disposable pattern.
  Public Sub Dispose() Implements IDisposable.Dispose
    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    Dispose(True)
    GC.SuppressFinalize(Me)
  End Sub
#End Region

End Class

